| @@ -2,6 +2,8 @@ module Agents | ||
| 2 | 2 | class PostAgent < Agent | 
| 3 | 3 | include WebRequestConcern | 
| 4 | 4 |  | 
| 5 | + MIME_RE = /\A\w+\/.+\z/ | |
| 6 | + | |
| 5 | 7 | can_dry_run! | 
| 6 | 8 | no_bulk_receive! | 
| 7 | 9 | default_schedule "never" | 
| @@ -13,7 +15,13 @@ module Agents | ||
| 13 | 15 |  | 
| 14 | 16 | The `method` used can be any of `get`, `post`, `put`, `patch`, and `delete`. | 
| 15 | 17 |  | 
| 16 | - By default, non-GETs will be sent with form encoding (`application/x-www-form-urlencoded`). Change `content_type` to `json` to send JSON instead. Change `content_type` to `xml` to send XML, where the name of the root element may be specified using `xml_root`, defaulting to `post`. | |
| 18 | + By default, non-GETs will be sent with form encoding (`application/x-www-form-urlencoded`). | |
| 19 | + | |
| 20 | + Change `content_type` to `json` to send JSON instead. | |
| 21 | + | |
| 22 | + Change `content_type` to `xml` to send XML, where the name of the root element may be specified using `xml_root`, defaulting to `post`. | |
| 23 | + | |
| 24 | + When `content_type` contains a [MIME](https://en.wikipedia.org/wiki/Media_type) type, and `payload` is a string, its interpolated value will be sent as a string in the HTTP request's body and the request's `Content-Type` HTTP header will be set to `content_type`. When `payload` is a string `no_merge` has to be set to `true`. | |
| 17 | 25 |  | 
| 18 | 26 | If `emit_events` is set to `true`, the server response will be emitted as an Event and can be fed to a WebsiteAgent for parsing (using its `data_from_event` and `type` options). No data processing | 
| 19 | 27 | will be attempted by this Agent, so the Event's "body" value will always be raw text. | 
| @@ -49,7 +57,8 @@ module Agents | ||
| 49 | 57 |            'something' => 'the event contained {{ somekey }}' | 
| 50 | 58 | }, | 
| 51 | 59 |          'headers' => {}, | 
| 52 | - 'emit_events' => 'false' | |
| 60 | + 'emit_events' => 'false', | |
| 61 | + 'no_merge' => 'false' | |
| 53 | 62 | } | 
| 54 | 63 | end | 
| 55 | 64 |  | 
| @@ -66,10 +75,19 @@ module Agents | ||
| 66 | 75 | errors.add(:base, "post_url and expected_receive_period_in_days are required fields") | 
| 67 | 76 | end | 
| 68 | 77 |  | 
| 69 | - if options['payload'].present? && !options['payload'].is_a?(Hash) | |
| 78 | + if options['payload'].present? && %w[get delete].include?(method) && !options['payload'].is_a?(Hash) | |
| 70 | 79 | errors.add(:base, "if provided, payload must be a hash") | 
| 71 | 80 | end | 
| 72 | 81 |  | 
| 82 | + if options['payload'].present? && %w[post put patch].include?(method) | |
| 83 | + if !options['payload'].is_a?(Hash) && options['content_type'] !~ MIME_RE | |
| 84 | + errors.add(:base, "if provided, payload must be a hash") | |
| 85 | + end | |
| 86 | + if options['content_type'] =~ MIME_RE && options['payload'].is_a?(String) && boolify(options['no_merge']) != true | |
| 87 | + errors.add(:base, "when the payload is a string, `no_merge` has to be set to `true`") | |
| 88 | + end | |
| 89 | + end | |
| 90 | + | |
| 73 | 91 |        if options.has_key?('emit_events') && boolify(options['emit_events']).nil? | 
| 74 | 92 | errors.add(:base, "if provided, emit_events must be true or false") | 
| 75 | 93 | end | 
| @@ -116,13 +134,16 @@ module Agents | ||
| 116 | 134 | when 'post', 'put', 'patch' | 
| 117 | 135 | params = nil | 
| 118 | 136 |  | 
| 119 | - case interpolated(payload)['content_type'] | |
| 137 | + case (content_type = interpolated(payload)['content_type']) | |
| 120 | 138 | when 'json' | 
| 121 | 139 | headers['Content-Type'] = 'application/json; charset=utf-8' | 
| 122 | 140 | body = data.to_json | 
| 123 | 141 | when 'xml' | 
| 124 | 142 | headers['Content-Type'] = 'text/xml; charset=utf-8' | 
| 125 | 143 | body = data.to_xml(root: (interpolated(payload)[:xml_root] || 'post')) | 
| 144 | + when MIME_RE | |
| 145 | + headers['Content-Type'] = content_type | |
| 146 | + body = data.to_s | |
| 126 | 147 | else | 
| 127 | 148 | body = data | 
| 128 | 149 | end | 
| @@ -46,6 +46,8 @@ describe Agents::PostAgent do | ||
| 46 | 46 | req.data = ActiveSupport::JSON.decode(request.body) | 
| 47 | 47 | when 'text/xml' | 
| 48 | 48 | req.data = Hash.from_xml(request.body) | 
| 49 | + when Agents::PostAgent::MIME_RE | |
| 50 | + req.data = request.body | |
| 49 | 51 | else | 
| 50 | 52 |            raise "unexpected Content-Type: #{content_type}" | 
| 51 | 53 | end | 
| @@ -187,6 +189,16 @@ describe Agents::PostAgent do | ||
| 187 | 189 | expect(@sent_requests[:get][0].data).to eq(@checker.options['payload'].to_query) | 
| 188 | 190 | end | 
| 189 | 191 |  | 
| 192 | + it "sends options['payload'] as a string POST request when content-type continas a MIME type" do | |
| 193 | + @checker.options['payload'] = '<test>hello</test>' | |
| 194 | + @checker.options['content_type'] = 'application/xml' | |
| 195 | +      expect { | |
| 196 | + @checker.check | |
| 197 | +      }.to change { @sent_requests[:post].length }.by(1) | |
| 198 | + | |
| 199 | +      expect(@sent_requests[:post][0].data).to eq('<test>hello</test>') | |
| 200 | + end | |
| 201 | + | |
| 190 | 202 | describe "emitting events" do | 
| 191 | 203 | context "when emit_events is not set to true" do | 
| 192 | 204 | it "does not emit events" do | 
| @@ -304,6 +316,25 @@ describe Agents::PostAgent do | ||
| 304 | 316 | expect(@checker).to be_valid | 
| 305 | 317 | end | 
| 306 | 318 |  | 
| 319 | + it "should not validate payload as a hash if content_type includes a MIME type and method is not get or delete" do | |
| 320 | + @checker.options['no_merge'] = 'true' | |
| 321 | + @checker.options['content_type'] = 'text/xml' | |
| 322 | + @checker.options['payload'] = "test" | |
| 323 | + expect(@checker).to be_valid | |
| 324 | + | |
| 325 | + @checker.options['method'] = 'get' | |
| 326 | + expect(@checker).not_to be_valid | |
| 327 | + | |
| 328 | + @checker.options['method'] = 'delete' | |
| 329 | + expect(@checker).not_to be_valid | |
| 330 | + end | |
| 331 | + | |
| 332 | + it "requires `no_merge` to be set to true when content_type contains a MIME type" do | |
| 333 | + @checker.options['content_type'] = 'text/xml' | |
| 334 | + @checker.options['payload'] = "test" | |
| 335 | + expect(@checker).not_to be_valid | |
| 336 | + end | |
| 337 | + | |
| 307 | 338 | it "requires headers to be a hash, if present" do | 
| 308 | 339 | @checker.options['headers'] = [1,2,3] | 
| 309 | 340 | expect(@checker).not_to be_valid |